#include "04-encapsulation.h"

/*
 * 全局变量g_val可以被多个线程同时访问，可以被多个线程访问是共享资源，
 * 多个线程对其进行操作，可能会出现问题：
 *
 * 下面模拟抢票的过程，多个线程对共享资源tickets做–的过程: 出现负数。
 *
 * 全局变量在没有保护的时候，往往是不安全的， 解决这种问题的办法就是加锁！
 */
 
using namespace std;

//共享资源
int tickets = 1000;

void* get_ticket(void* args)
{
    std::string name = static_cast<const char *>(args);
    while(true)
    {
        if(tickets>0)
        {
            usleep(1234);
            cout<<name<<"正在抢票 : "<<tickets<<endl;
            tickets--;
        }
        else
        {
            break;
        }
    }
    return nullptr;
}
int main()
{
    std::unique_ptr<Thread> thread1(new Thread(get_ticket,(void*)"hellothread",1));
    std::unique_ptr<Thread> thread2(new Thread(get_ticket,(void*)"COUTthread",2));
    std::unique_ptr<Thread> thread3(new Thread(get_ticket,(void*)"PRINTthread",3));
    std::unique_ptr<Thread> thread4(new Thread(get_ticket,(void*)"TESTthread",4));

    thread1->join();
    thread2->join();
    thread3->join();
    thread4->join();
    return 0;
}

#if 0
正在抢票 : 27
PRINTthread正在抢票 : 26
TESTthread正在抢票 : 25
COUTthread正在抢票 : 24
hellothread正在抢票 : 23
PRINTthread正在抢票 : 22
TESTthread正在抢票 : 21
COUTthread正在抢票 : 20
hellothread正在抢票 : 19
hellothread正在抢票 : 18
TESTthread正在抢票 : 17
COUTthread正在抢票 : 16PRINTthread
正在抢票 : 15
COUTthread正在抢票 : 14
PRINTthread正在抢票 : 14
TESTthread正在抢票 : 13
hellothread正在抢票 : 11
COUTthread正在抢票 : 10
TESTthread正在抢票 : 9
PRINTthread正在抢票 : 8
hellothread正在抢票 : 7
COUTthread正在抢票 : 6
hellothread正在抢票 : 5
TESTthread正在抢票 : 4
PRINTthread正在抢票 : 3
COUTthread正在抢票 : 2
hellothread正在抢票 : 1
TESTthread正在抢票 : 0
PRINTthread正在抢票 : -1
COUTthread正在抢票 : -2
#endif

#if 0
结果出现负数:

 如果需要出现负数的现象：尽可能让多个线程交叉执行，多个线程交叉执行的本质：让调度器尽可能的频繁发生线程调度与切换
 线程一般什么时候发生线程切换：时间片到了或者来了更高优先级的线程或者线程等待的时候。
 线程什么时候检测上面的问题：从内核态返回用户态的时候，线程要对调度状态进行检测，如果可以就直接发生线程切换
 出现负数的票就是因为多个线程交叉执行，多个线程交叉执行的本质：让调度器尽可能频繁的发生线程的调度与切换
 在tickets==1时，所有进程都可以进去，然后在判断：1.读取内存数据cpu内的寄存器中2.进行判断；第一个线程判断是大于0的,此时线程会被切走，寄存器只有一个，寄存器的内容是当前执行流的上下文，会把上下文带走，还没来得及tickets–，其他线程看到的tickets也是1，也要保存自己的上下文…ticket减减前线程都会休眠一会，当一个线程唤醒tickets–
 --的本质就是1.读取数据2.更改数据3.写回数据


 对一个全局变量进行多线程更改是不安全的：

 对变量进行++或者–-，在C、C++上看起来只有一条语句，但是汇编之后至少是三条语句：
 1.从内存读取数据到CPU寄存器中
 2.在寄存器中让CPU进行对应的算逻运算
 3.写回新的结果到内存中变量的位置

 现在线程1把数据加载到寄存器中，做–，成为999，
 到第三步的时候写回到内存的时候很不幸被切走了，把上下文顺便也卷走了：

此时调度线程2，线程2很开心，一直在–，到1tickets变为100的时候，
内存中变量的也变为了100，但是当它想继续–的时候，线程2倍切走了，
带着自己的上下文走了，现在线程1回来了：恢复上下文，继续之前的第三步，
此时线程2好不容易把tickets变为100，但是被线程1改为了999

又变成了999，造成了干扰

此可知我们定义的全局变量在没有保护的时候，往往是不安全的，
像上面的例子，多个线程交替执行时造成数据安全问题，发生了数据不一致问题。

而解决这种问题的办法就是加锁！


#endif
